
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>

struct timeval inicio,final;

/* Variaveis para calculo do valor aproximado de PI */
mpf_t pi;
mpf_t pontosTotal;
mpf_t pontosValidos;
mpf_t tempEstaCirc;
mpf_t x;
mpf_t y;


unsigned int ptosTotal=0;
unsigned int ptosValidos=0;

/* Destroi as variaveis da biblioteca GMP utilizadas para calcular o valor de PI */
void destrutorMonteCarlo(){
    mpf_clear(pi);
    mpf_clear(pontosTotal);
    mpf_clear(pontosValidos);
    mpf_clear(tempEstaCirc);
    mpf_clear(x);
    mpf_clear(y);
}

/* Define a precisão do ponto flutuante da biblioteca GMP e inicializa as variaveis */
void iniciaMonteCarlo(){
    mpf_set_default_prec (340000000);
    mpf_init(pi);
    mpf_init(pontosTotal);
    mpf_init(pontosValidos);
    mpf_init2(tempEstaCirc, 1000);
    mpf_init2(x, 1000);
    mpf_init2(y, 1000);
}

/* Calculo do valor aproximado do PI. Foi utilizado a forma mais simples para o calculo PI = 4 * (PtosDentroDaCircunferencia/PtosTotais) */
void calculaPiMonteCarlo(){
    mpf_set_ui(pontosValidos, ptosValidos);
    mpf_set_ui(pontosTotal, ptosTotal);
    mpf_div(pi, pontosValidos, pontosTotal);
    mpf_mul_ui(pi, pi, 4);
}

/* Calcula o tempo e chama as funcoes para inicializar as variaveis, destrui-las e para calcular o valor aproximado do PI e faz a selecao
aleatoria dos pontos.
*/
void MonteCarloSeq(int numPontos){
    FILE *MonteCarloSeq;
    MonteCarloSeq = fopen("monteCarloSequencial.txt", "w+");
    gmp_randstate_t state;

    int tempo1;
    int tempo2;

    int i;

    /* marca tempo inicial de execucao */
    gettimeofday(&inicio, NULL);

    /* funcao da GMP para numeros randomicos */
    gmp_randinit_mt(state);
    gmp_randseed_ui(state, time(NULL));

    /* inicia as variaveis */
    iniciaMonteCarlo();

    for(i=0; i<numPontos; i++){
        /* funcao da GMP que seleciona numeros aleatorios entre 0 e 1 */
        mpf_urandomb(x, state, 100);
        mpf_urandomb(y, state, 100);
        mpf_mul(x, x, x);
        mpf_mul(y, y, y);
        mpf_add(tempEstaCirc, x, y);
        /* verifica se o ponto esta dentro da circunferencia */
        if(mpf_cmp_ui(tempEstaCirc, 1) <= 0){
            ptosValidos++;
        }
        ptosTotal++;
    }

    /* calcula o valor aproximado de PI */
    calculaPiMonteCarlo();

    /* marca tempo final da execucao */
    gettimeofday(&final, NULL);

    /* mostra valor aproximado de PI */
    gmp_printf("pi calculado = %.10000000Ff\n",pi);
    mpf_out_str(MonteCarloSeq,10,10000000,pi);

    /* calcula tempo em milisegundos e segundos */
	tempo1 = final.tv_usec - inicio.tv_usec;
	tempo2 = final.tv_sec - inicio.tv_sec;

	/* mostra informacoes adicionais */
	printf("Contador de iterações %i\n\n", ptosTotal);
    printf("Tempo em %i milisegundos\n",tempo1);
    printf("Tempo em %i segundos\n",tempo2);
    printf("Pontos Dentro: %d       Pontos Total: %d\n\n\n", ptosValidos, ptosTotal);

    /* fecha arquivo com o valor aproximado de PI */
    fclose(MonteCarloSeq);
    /* destroi as variaveis da GMP */
    destrutorMonteCarlo();

}
